/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2023. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file ac780x_pwm.c
 *
 * @brief This file provides pulse width modulation module integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_pwm_reg.h"

/* ============================================  Define  ============================================ */

/* ===========================================  Typedef  ============================================ */
/* This structure holds the clock/reset/irq info */
typedef struct
{
    CKGEN_ClockType clock;      /* clock */
    CKGEN_SoftResetType reset;  /* reset */
    IRQn_Type irq;              /* irq */
} PWM_InfoType;

/* ==========================================  Variables  =========================================== */
/* PWM related info */
static const PWM_InfoType s_pwmInfo[PWM_INSTANCE_MAX] =
{
    {CLK_PWM0, SRST_PWM0, PWM0_IRQn},
    {CLK_PWM1, SRST_PWM1, PWM1_IRQn},
    {CLK_PWM2, SRST_PWM2, PWM2_IRQn},
};

/* PWM callback pointer */
static DeviceCallback_Type s_pwmCallback[PWM_INSTANCE_MAX] = {NULL};

/* PWM capture opreate mode */
static PWM_OperateModeType s_pwmMode[PWM_INSTANCE_MAX] =
{
    PWM_MODE_MODULATION,
    PWM_MODE_MODULATION,
    PWM_MODE_MODULATION
};

/* ====================================  Functions declaration  ===================================== */
static void PWM_SetPWMMode(PWM_Type *PWMx, const PWM_ModulationConfigType *config);
static void PWM_SetInputCaptureMode(PWM_Type* PWMx, const PWM_inputCaptureConfigType *config);
static void PWM_SetOutputCompareMode(PWM_Type *PWMx, const PWM_OutputCompareConfigType *config);
static void PWM_SetQuadDecodeMode(PWM_Type *PWMx, const PWM_QuadDecodeConfigType *config);

/* ======================================  Functions define  ======================================== */
/*!
 * @brief PWM initialize.
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @param[in] config: pointer to configuration structure
 * @return none
 */
void PWM_Init(PWM_Type *PWMx, const PWM_ConfigType *config)
{
    uint8_t instance = 0U;

    instance = PWM_INDEX(PWMx);

    DEVICE_ASSERT(PWM_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != config);

    /* Enbale clock */
    CKGEN_Enable(s_pwmInfo[instance].clock, ENABLE);
    CKGEN_SoftReset(s_pwmInfo[instance].reset, ENABLE);

    /* Set period value */
    PWM_SetClockPrescaler(PWMx, config->clkPsc);
    PWM_SetCounterInitValue(PWMx, config->initValue);
    PWM_SetCounter(PWMx, config->initValue);
    PWM_SetMaxCountValue(PWMx, config->maxValue);

    /* Register callback function */
    s_pwmCallback[instance] = config->callback;

    /* Store PWM operate mode */
    s_pwmMode[instance] = config->mode;

    PWM_SetCNTOFFrequence(PWMx, config->cntOverflowFreq);

    /* Set interrupt */
    PWM_SetOverflowInterrupt(PWMx, config->overflowInterrupEn);
    if (ENABLE == config->interruptEn)
    {
        NVIC_EnableIRQ(s_pwmInfo[instance].irq);
    }
    else
    {
        NVIC_DisableIRQ(s_pwmInfo[instance].irq);
    }

    /* Initialize according to different mode */
    switch (config->mode)
    {
    case PWM_MODE_MODULATION:
        PWM_SetPWMMode(PWMx, config->initModeStruct);
        break;

    case PWM_MODE_INPUT_CAPTURE:
        PWM_SetInputCaptureMode(PWMx, config->initModeStruct);
        break;

    case PWM_MODE_OUTPUT_COMPARE:
        PWM_SetOutputCompareMode(PWMx, config->initModeStruct);
        break;

    case PWM_MODE_QUADRATURE_DECODER:
        PWM_SetQuadDecodeMode(PWMx, config->initModeStruct);
        break;

    case PWM_MODE_GENERAL_TIMER:
        /* Do nothing */
        break;

    default:
        break;
    }

    /* Set clock source causes the counter to start working,If you don't want to start immediately after initialization
    you can disable it by passing PWM_CLK_SOURCE_NONE parameters */
    PWM_SetClockSource(PWMx, config->clkSource);
}

/*!
 * @brief PWM De-initialize.
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @return none
 */
void PWM_DeInit(PWM_Type *PWMx)
{
    uint8_t instance = 0U;

    instance = PWM_INDEX(PWMx);

    DEVICE_ASSERT(PWM_INSTANCE_MAX > instance);

    NVIC_DisableIRQ(s_pwmInfo[instance].irq);
    NVIC_ClearPendingIRQ(s_pwmInfo[instance].irq);

    CKGEN_SoftReset(s_pwmInfo[instance].reset, DISABLE);
    CKGEN_Enable(s_pwmInfo[instance].clock, DISABLE);
}

/*!
 * @brief Set pwm module work in pulse width modulation mode.
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @param[in] config: pointer to configuration structure
 * @return none
 */
static void PWM_SetPWMMode(PWM_Type *PWMx, const PWM_ModulationConfigType *config)
{
    uint8_t i = 0U, pairChannel = 0U;
    PWM_ChannelType channel;

    DEVICE_ASSERT(NULL != config);

    /* Configure independent channels */
    DEVICE_ASSERT(PWM_CHANNEL_MAX > config->independentChannelNum);
    for (i = 0U; i < config->independentChannelNum; i++)
    {
        channel = config->independentChConfig[i].channel;
        PWM_SetChannelOutputInitLevel(PWMx, channel, config->independentChConfig[i].initLevel);
    }

    /* Configure combined channels */
    DEVICE_ASSERT(PWM_PAIR_CHANNEL_NUM > config->combineChannelNum);
    for (i = 0U; i < config->combineChannelNum; i++)
    {
        channel = config->combineChConfig[i].pairChannel;
        PWM_SetChannelOutputInitLevel(PWMx, channel, config->combineChConfig[i].ch1stInitLevel);
        PWM_SetChannelOutputInitLevel(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), config->combineChConfig[i].ch2ndInitLevel);
    }

    /* Set init output , the initialization feature must be used only with disabled counter*/
    PWM_SetInitChannelOutput(PWMx, config->initChOutputEn);

    /* Set counting mode */
    PWM_SetCountMode(PWMx, config->countMode);

    /* Configure independent channels */
    for (i = 0U; i < config->independentChannelNum; i++)
    {
        channel = config->independentChConfig[i].channel;
        PWM_SetChannelMSR(PWMx, channel, 2U);
        PWM_SetChannelELSR(PWMx, channel, (uint8_t)config->independentChConfig[i].levelMode);
        PWM_SetChannelCountValue(PWMx, channel, config->independentChConfig[i].chValue);
        PWM_SetChannelPolarity(PWMx, channel, config->independentChConfig[i].polarity);
        PWM_SetChannelInterrupt(PWMx, channel, config->independentChConfig[i].interruptEn);
        PWM_SetChannelTrigger(PWMx, channel, config->independentChConfig[i].triggerEn);
    }

    /* Configure combined channels */
    for (i = 0U; i < config->combineChannelNum; i++)
    {
        channel = config->combineChConfig[i].pairChannel;

        /* Check the channel is even number */
        DEVICE_ASSERT(0 == (channel % 2U));
        pairChannel = (uint8_t)(channel) >> 1U;

        PWM_SetPairChannelCombine(PWMx, pairChannel, ENABLE);
        PWM_SetPairChannelDeadtime(PWMx, pairChannel, config->combineChConfig[i].deadtimeEn);
        PWM_SetPairChannelComplement(PWMx, pairChannel, config->combineChConfig[i].complementEn);

        PWM_SetDeadtime(PWMx, pairChannel, config->combineChConfig[i].deadtimePsc, config->combineChConfig[i].deadtime);

        PWM_SetChannelELSR(PWMx, channel, (uint8_t)config->combineChConfig[i].levelMode);

        PWM_SetChannelCountValue(PWMx, channel, config->combineChConfig[i].ch1stValue);
        PWM_SetChannelCountValue(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), config->combineChConfig[i].ch2ndValue);

        PWM_SetChannelMatchDir(PWMx, channel, config->combineChConfig[i].ch1stMatchDir);
        PWM_SetChannelMatchDir(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), config->combineChConfig[i].ch2ndMatchDir);

        PWM_SetChannelPolarity(PWMx, channel, config->combineChConfig[i].ch1stPolarity);
        PWM_SetChannelPolarity(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), config->combineChConfig[i].ch2ndPolarity);

        PWM_SetChannelInterrupt(PWMx, channel, config->combineChConfig[i].ch1stInterruptEn);
        PWM_SetChannelInterrupt(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), config->combineChConfig[i].ch2ndInterruptEn);

        PWM_SetChannelTrigger(PWMx, channel, config->combineChConfig[i].ch1stTriggerEn);
        PWM_SetChannelTrigger(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), config->combineChConfig[i].ch2ndTriggerEn);
    }

    PWM_SetInitTrigger(PWMx, config->initTriggerEn);
}

/*!
 * @brief Set pwm module work in input capture mode.
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @param[in] config: pointer to configuration structure
 * @return none
 */
static void PWM_SetInputCaptureMode(PWM_Type *PWMx, const PWM_inputCaptureConfigType *config)
{
    uint8_t i = 0U, pairChannel = 0U;
    PWM_ChannelType channel;

    DEVICE_ASSERT(NULL != config);

    /* Set counting mode */
    PWM_SetCountMode(PWMx, PWM_UP_COUNT);

    /* Configure input capture channels */
    DEVICE_ASSERT(PWM_CHANNEL_MAX > config->channelNum);

    for (i = 0U; i < config->channelNum; i++)
    {
        channel = config->channelConfig[i].channel;

        /* Single edge capture mode channel config */
        if (PWM_INPUTCAP_SINGLE_EDGE == config->channelConfig[i].mode)
        {
            PWM_SetChannelMSR(PWMx, channel, 0U);
            PWM_SetChannelELSR(PWMx, channel, (uint8_t)config->channelConfig[i].detectType);
            PWM_SetCaptureEventPrescaler(PWMx, channel, config->channelConfig[i].eventPsc);
            PWM_SetChannelInterrupt(PWMx, channel, config->channelConfig[i].interruptEn);
        }
        else /* Dual edge capture mode channel config */
        {
            /* Check the channel is even number */
            DEVICE_ASSERT(0U == (channel % 2U));
            pairChannel = (uint8_t)(channel) >> 1U;

            PWM_SetPairChannelDualEdgeCapture(PWMx, pairChannel, ENABLE);
            PWM_SetPairChannelDecap(PWMx, pairChannel, ENABLE);
            PWM_SetChannelMSR(PWMx, channel, (uint8_t)config->channelConfig[i].onceMode);

            /* Set measure type */
            switch (config->channelConfig[i].measureType)
            {
            case PWM_POSITIVE_PULSE_WIDTH_MEASURE:
                PWM_SetChannelELSR(PWMx, channel, 1U);
                PWM_SetChannelELSR(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), 2U);
                break;

            case PWM_NEGATIVE_PULSE_WIDTH_MEASURE:
                PWM_SetChannelELSR(PWMx, channel, 2U);
                PWM_SetChannelELSR(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), 1U);
                break;

            case PWM_RISING_EDGE_PERIOD_MEASURE:
                PWM_SetChannelELSR(PWMx, channel, 1U);
                PWM_SetChannelELSR(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), 1U);
                break;

            case PWM_FALLING_EDGE_PERIOD_MEASURE:
                PWM_SetChannelELSR(PWMx, channel, 2U);
                PWM_SetChannelELSR(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), 2U);
                break;

            default:
                break;
            }

            /* Just set channel n+1 interrupt */
            PWM_SetChannelInterrupt(PWMx, (PWM_ChannelType)((uint8_t)channel + 1U), config->channelConfig[i].interruptEn);
        }

        /* Enable filtering for input channels */
        DEVICE_ASSERT(PWM_CH_4 > channel);
        if (ENABLE == config->channelConfig[i].filterEn)
        {
            PWM_SetChannelInputFilter(PWMx, channel, config->channelConfig[i].filterValue);
        }
        else
        {
            PWM_SetChannelInputFilter(PWMx, channel, 0U);
        }
    }
}

/*!
 * @brief Set pwm module work in output compare mode.
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @param[in] config: pointer to configuration structure
 * @return none
 */
static void PWM_SetOutputCompareMode(PWM_Type *PWMx, const PWM_OutputCompareConfigType *config)
{
    uint8_t i = 0U;
    PWM_ChannelType channel;

    DEVICE_ASSERT(NULL != config);

    DEVICE_ASSERT(PWM_CHANNEL_MAX > config->channelNum);
    for (i = 0U; i < config->channelNum; i++)
    {
        channel = config->channelConfig[i].channel;
        PWM_SetChannelOutputInitLevel(PWMx, channel, config->channelConfig[i].initLevel);
    }
    PWM_SetInitChannelOutput(PWMx, config->initChOutputEn);

    /* Set counting mode */
    PWM_SetCountMode(PWMx, PWM_UP_COUNT);

    /* Configure output compare channels */
    DEVICE_ASSERT(PWM_CHANNEL_MAX > config->channelNum);
    for (i = 0U; i < config->channelNum; i++)
    {
        channel = config->channelConfig[i].channel;

        PWM_SetChannelMSR(PWMx, channel, 1U);
        PWM_SetChannelELSR(PWMx, channel, (uint8_t)config->channelConfig[i].mode);
        PWM_SetChannelCountValue(PWMx, channel, config->channelConfig[i].comparedValue);
        PWM_SetChannelInterrupt(PWMx, channel, config->channelConfig[i].interruptEn);
        PWM_SetChannelTrigger(PWMx, channel, config->channelConfig[i].triggerEn);
    }

}

/*!
 * @brief Set pwm module work in quadrature decoder mode.
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @param[in] config: pointer to configuration structure
 * @return none
 */
static void PWM_SetQuadDecodeMode(PWM_Type *PWMx, const PWM_QuadDecodeConfigType *config)
{
    DEVICE_ASSERT(PWM_INSTANCE_MAX > PWM_INDEX(PWMx));
    DEVICE_ASSERT(NULL != config);

    /* Set quadrature encoding mode */
    PWM_SetQuadEncodeMode(PWMx, config->mode);

    /* Set polarity for Phase A and Phase B */
    PWM_SetQuadPhaseAPolarity(PWMx, config->phaseAConfig.polarity);
    PWM_SetQuadPhaseBPolarity(PWMx, config->phaseBConfig.polarity);

    /* Set Phase filter */
    if (config->phaseAConfig.filterEn)
    {
        /* phaseA corresponding channel 0 */
        PWM_SetChannelInputFilter(PWMx, PWM_CH_0, config->phaseAConfig.filterVal);
    }
    else
    {
        PWM_SetChannelInputFilter(PWMx, PWM_CH_0, 0U);
    }

    if (config->phaseBConfig.filterEn)
    {
        /* phaseB corresponding channel 1 */
        PWM_SetChannelInputFilter(PWMx, PWM_CH_1, config->phaseBConfig.filterVal);
    }
    else
    {
        PWM_SetChannelInputFilter(PWMx, PWM_CH_1, 0U);
    }

    /* Set quadrature decoder mode */
    PWM_SetQuadDecode(PWMx, config->quadEn);
}

/*!
 * @brief Init fault control.
 *
 * Use only in PWM Modulation mode,Called after PWM_Init function
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @param[in] config: pointer to configuration structure
 * @return none
 */
void PWM_InitFaultControl(PWM_Type *PWMx, const PWM_FaultConfig *config)
{
    uint8_t i = 0U;
    PWM_FaultChannelType channel = PWM_FAULT_CH_0;

    DEVICE_ASSERT(PWM_INSTANCE_MAX > PWM_INDEX(PWMx));
    DEVICE_ASSERT(NULL != config);

    PWM_SetFaultMode(PWMx, config->mode);
    PWM_SetFaultInputFilterValue(PWMx, config->filterValue);
    PWM_SetFaultInterrupt(PWMx, config->interruptEn);

    for (i = 0U; i < PWM_PAIR_CHANNEL_NUM; i++)
    {
        PWM_SetPairChannelFaultControl(PWMx, i, config->faultCtrlOutputEn[i]);
    }

    for (channel = PWM_FAULT_CH_0; channel < PWM_FAULT_CH_MAX; channel++)
    {
        PWM_SetFaultInputFilter(PWMx, channel, config->channelConfig[channel].faultFilterEn);
        PWM_SetFaultInputPolarity(PWMx, channel, config->channelConfig[channel].faultPolarity);
        PWM_SetFaultInput(PWMx, channel, config->channelConfig[channel].faultInputEn);
    }
}

/*!
 * @brief Init synchronization control function.
 *
 * called after PWM_Init function
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @param[in] config: pointer to configuration structure
 * @return none
 */
void PWM_InitSyncControl(PWM_Type *PWMx, const PWM_SyncConfigType *config)
{
    uint8_t i = 0U;

    DEVICE_ASSERT(PWM_INSTANCE_MAX > PWM_INDEX(PWMx));
    DEVICE_ASSERT(NULL != config);

    /* Set sync trigger mode */
    if (PWM_SYNC_TRIGGER_SOFTWARE == config->syncTriggerMode)
    {
        PWM_SetMCVRSyncTriggerMode(PWMx, PWM_SYNC_TRIGGER_SOFTWARE, ENABLE);
        PWM_SetOMCRSyncTriggerMode(PWMx, PWM_SYNC_TRIGGER_SOFTWARE, ENABLE);
        PWM_SetINVCRSyncTriggerMode(PWMx, PWM_SYNC_TRIGGER_SOFTWARE, ENABLE);
        PWM_SetCHOSWCRSyncTriggerMode(PWMx, PWM_SYNC_TRIGGER_SOFTWARE, ENABLE);
        PWM_SetCHOPOLCRSyncTriggerMode(PWMx, PWM_SYNC_TRIGGER_SOFTWARE, ENABLE);
    }
    else /* hardware trigger mode */
    {
        PWM_SetMCVRSyncTriggerMode(PWMx, PWM_SYNC_TRIGGER_HARDWARE, ENABLE);
        PWM_SetOMCRSyncTriggerMode(PWMx, PWM_SYNC_TRIGGER_HARDWARE, ENABLE);
        PWM_SetINVCRSyncTriggerMode(PWMx, PWM_SYNC_TRIGGER_HARDWARE, ENABLE);
        PWM_SetCHOSWCRSyncTriggerMode(PWMx, PWM_SYNC_TRIGGER_HARDWARE, ENABLE);
        PWM_SetCHOPOLCRSyncTriggerMode(PWMx, PWM_SYNC_TRIGGER_HARDWARE, ENABLE);
    }

    PWM_SetOMCRSync(PWMx, config->outmaskSyncEn);
    PWM_SetCNTINSync(PWMx, config->counterInitSyncEn);
    PWM_SetCHOSWCRSync(PWMx, config->outCtrlSyncEn);
    PWM_SetINVCRSync(PWMx, config->inverterSyncEn);
    PWM_SetCHOPOLCRSync(PWMx, config->polaritySyncEn);

    for (i = 0U; i < PWM_PAIR_CHANNEL_NUM; i++)
    {
        PWM_SetPairChannelCounterSync(PWMx, i, config->chValueSyncEn[i]);
    }

    PWM_SetHardwareTriggerSyncSrc(PWMx, 0U, config->hardwareSync0En);
    PWM_SetHardwareTriggerSyncSrc(PWMx, 1U, config->hardwareSync1En);
    PWM_SetHardwareTriggerSyncSrc(PWMx, 2U, config->hardwareSync2En);

    if ((DISABLE == config->maxLoadingPointEn) && (DISABLE == config->minLoadingPointEn))
    {
        /* update immediately when no load point is set */
        PWM_SetCNTSyncTriggerMode(PWMx, config->syncTriggerMode, ENABLE);
        PWM_SetMaxLoadingPoint(PWMx, DISABLE);
        PWM_SetMinLoadingPoint(PWMx, DISABLE);
    }
    else
    {
        PWM_SetCNTSyncTriggerMode(PWMx, config->syncTriggerMode, DISABLE);
        PWM_SetMaxLoadingPoint(PWMx, config->maxLoadingPointEn);
        PWM_SetMinLoadingPoint(PWMx, config->minLoadingPointEn);
    }

    PWM_SetHwTriggerSyncAutoClear(PWMx, config->autoClearHWTriggerEn);
    PWM_SetSyncType(PWMx, 0U);
    PWM_SetSyncMode(PWMx, PWM_SYNC_MODE_ENHANCED);
    PWM_SetSync(PWMx, config->syncEn);
}

/*!
 * @brief PWM simply initialize.
 *
 * Easy for users to simply config pwm mode
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @param[in] config: pointer to configuration structure
 * @return none
 */
void PWM_SimplyInit(PWM_Type *PWMx, const PWM_SimplyConfigType *config)
{
    uint8_t instance = 0U, i = 0U;

    instance = PWM_INDEX(PWMx);

    DEVICE_ASSERT(PWM_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != config);

    /* Enbale clock */
    CKGEN_Enable(s_pwmInfo[instance].clock, ENABLE);
    CKGEN_SoftReset(s_pwmInfo[instance].reset, ENABLE);

    for (i = 0U; i < PWM_CHANNEL_MAX; i++)
    {
        if (i % 2U)
        {
            /* odd channel */
            PWM_SetChannelOutputInitLevel(PWMx, (PWM_ChannelType)(i), config->oddInitLevel);
        }
        else
        {
            /* even channel */
            PWM_SetChannelOutputInitLevel(PWMx, (PWM_ChannelType)(i), config->evenInitLevel);
        }
    }

    /* Set init output , the initialization feature must be used only with disabled counter*/
    PWM_SetInitChannelOutput(PWMx, config->initChOutputEn);

    /* Set period value */
    PWM_SetClockPrescaler(PWMx, config->clkPsc);
    PWM_SetCounterInitValue(PWMx, config->initValue);
    PWM_SetCounter(PWMx, config->initValue);
    PWM_SetMaxCountValue(PWMx, config->maxValue);

    /* Set counting mode */
    PWM_SetCountMode(PWMx, config->countMode);

    /* Set channel  */
    for (i = 0U; i < PWM_PAIR_CHANNEL_NUM; i++)
    {
        if (PWM_COMBINE_MODE == config->allChCombineMode)
        {
            /* Combine channel mode */
            PWM_SetPairChannelCombine(PWMx, i, ENABLE);
            PWM_SetPairChannelDeadtime(PWMx, i, ENABLE);
        }
        else
        {
            /* Independent channel mode */
            PWM_SetPairChannelCombine(PWMx, i, DISABLE);
            PWM_SetPairChannelDeadtime(PWMx, i, DISABLE);
        }

        /* Set Deadtime*/
        PWM_SetDeadtime(PWMx, i, config->deadtimePsc, config->deadtime);

        PWM_SetPairChannelComplement(PWMx, i, config->complementEn);
    }

    for (i = 0U; i < PWM_CHANNEL_MAX; i++)
    {
        PWM_SetChannelELSR(PWMx, (PWM_ChannelType)(i), (uint8_t)config->levelMode);
        PWM_SetChannelMSR(PWMx, (PWM_ChannelType)(i), 2UL);
        PWM_SetChannelCountValue(PWMx, (PWM_ChannelType)(i), config->chValue[i]);

        if (i % 2U)
        {
            /* odd channel */
            PWM_SetChannelPolarity(PWMx, (PWM_ChannelType)(i), config->oddPolarity);
        }
        else
        {
            /* even channel */
            PWM_SetChannelPolarity(PWMx, (PWM_ChannelType)(i), config->evenPolarity);
        }
    }

    /* Register callback function */
    s_pwmCallback[instance] = config->callback;

    /* Store PWM operate mode */
    s_pwmMode[instance] = PWM_MODE_MODULATION;

    /* Set interrupt */
    PWM_SetOverflowInterrupt(PWMx, config->overflowInterrupEn);

    /* Set interrupt */
    if (ENABLE == config->interruptEn)
    {
        NVIC_EnableIRQ(s_pwmInfo[instance].irq);
    }
    else
    {
        NVIC_DisableIRQ(s_pwmInfo[instance].irq);
    }

    /* Set clock source causes the counter to start working,If you don't want to start immediately after initialization
    you can disable it by passing PWM_CLK_SOURCE_NONE parameters */
    PWM_SetClockSource(PWMx, config->clkSource);
}

/*!
 * @brief Set pwm callback function.
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @param[in] func: callback function
 * @return none
 */
void PWM_SetCallback(PWM_Type *PWMx, const DeviceCallback_Type func)
{
    uint8_t instance = 0U;

    instance = PWM_INDEX(PWMx);

    DEVICE_ASSERT(PWM_INSTANCE_MAX > instance);
    s_pwmCallback[instance] = func;
}

/*!
 * @brief PWM common interrupt service routine
 *
 * @param[in] PWMx: pwm module
 *               - PWM0
 *               - PWM1
 *               - PWM2
 * @return none
 */
static void PWM_CommonISR(PWM_Type *PWMx)
{
    uint8_t i = 0U;
    uint8_t instance = 0U;
    uint32_t wpara = 0U, lpara = 0U;

    instance = PWM_INDEX(PWMx);

    if (PWM_GetOverflowInterrupt(PWMx) && PWM_GetOverflowFlag(PWMx))
    {
        /* store overflow status */
        wpara = PWM_GetOverflowFlag(PWMx);

        /* clear overflow status */
        PWM_ClearOverflowFlag(PWMx);
    }
    else if (PWM_GetFaultInterrupt(PWMx) && PWM_GetFaultFlag(PWMx))
    {
        /* clear fault flag will affect the output behavior, so it is operated by the user */
    }
    else
    {
        /* store channel status and clear */
        lpara = PWM_GetAllChannelFlag(PWMx);

        /* handle channel flag */
        if (PWM_MODE_INPUT_CAPTURE == s_pwmMode[instance])
        {
            for (i = 0U; i < PWM_CHANNEL_MAX; i++)
            {
                if ((lpara >> i) & 0x01U)
                {
                    /* odd channel */
                    if (i % 2U)
                    {
                        /* channel is dual-capture */
                        if (PWM_GetPairChannelDualEdgeCapture(PWMx, (i >> 1U)))
                        {
                            /* clear both channel flag */
                            PWM_ClearChannelFlag(PWMx, i - 1U);
                            PWM_ClearChannelFlag(PWMx, i);
                        }
                        /* channel is single-capture */
                        else
                        {
                            /* clear channel flag */
                            PWM_ClearChannelFlag(PWMx, i);
                        }
                    }
                    /* even channel */
                    else
                    {
                        /* channel is dual-capture */
                        if (PWM_GetPairChannelDualEdgeCapture(PWMx, (i >> 1U)))
                        {
                            /* no need to clear flag */
                        }
                        /* channel is single-capture */
                        else
                        {
                            /* clear channel flag */
                            PWM_ClearChannelFlag(PWMx, i);
                        }
                    }
                }
            }
        }
        /* other mode */
        else
        {
            /* clear channel flag */
            PWM_ClearAllChannelFlag(PWMx, lpara);
        }
    }

    if (NULL != s_pwmCallback[instance])
    {
        /* callback */
        s_pwmCallback[instance](PWMx, wpara, lpara);
    }
}

/*!
 * @brief PWM0 interrupt request handler
 *
 * @param[in] none
 * @return none
 */
void PWM0_IRQHandler(void)
{
    PWM_CommonISR(PWM0);
}

/*!
 * @brief PWM1 interrupt request handler
 *
 * @param[in] none
 * @return none
 */
void PWM1_IRQHandler(void)
{
    PWM_CommonISR(PWM1);
}

/*!
 * @brief PWM2 interrupt request handler
 *
 * @param[in] none
 * @return none
 */
void PWM2_IRQHandler(void)
{
    PWM_CommonISR(PWM2);
}

/* =============================================  EOF  ============================================== */
